#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <SDL2/SDL.h>

#define VIDEO_DEV "/dev/video10"
#define WIDTH 1920
#define HEIGHT 1080
#define BUFFER_COUNT 4

struct buffer {
    void *start;
    size_t length;
};

// 检查 ioctl 是否出错
static void xioctl(int fd, int request, void *arg) {
    if (ioctl(fd, request, arg) < 0) {
        perror("ioctl");
        exit(1);
    }
}

__u32 getNalu(void *start) {
    static char buffer[20*1024*1024];
    static size_t length = 0;
    static size_t pos = 0;

    if (length == 0) {
        FILE *fp = fopen("input.h264", "rb");
        if (!fp) {
            perror("fopen");
            return 0;
        }

        length = fread(buffer, 1, 20*1024*1024, fp);
        fclose(fp);
    }

    if (pos == length) return 0;

    size_t start_pos = pos;
    pos += 4;

    char flag = 4;
    while (pos < length) {
        if (buffer[pos] == 0) {
            flag >>= 1;
        } else if (buffer[pos] == 1 && flag == 0) {
            pos -= 3;
            break;
        } else {
            flag = 4;
        }
        pos++;
    }

    memcpy(start, &buffer[start_pos], pos - start_pos);
    return pos - start_pos;
}

int main() {
    int fd = open(VIDEO_DEV, O_RDWR|O_NONBLOCK);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    // 设置 OUTPUT（压缩流）格式
    struct v4l2_format fmt = {0};
    fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    fmt.fmt.pix_mp.width = WIDTH;
    fmt.fmt.pix_mp.height = HEIGHT;
    fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
    fmt.fmt.pix_mp.num_planes = 1;
    xioctl(fd, VIDIOC_S_FMT, &fmt);

    // 请求 OUTPUT buffer
    struct v4l2_requestbuffers req = {
        .count = 1,
        .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
        .memory = V4L2_MEMORY_MMAP
    };
    xioctl(fd, VIDIOC_REQBUFS, &req);

    // mmap output buffers
    struct v4l2_buffer output_buf = {
        .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
        .memory = V4L2_MEMORY_MMAP,
        .index = 0
    };
    struct v4l2_plane output_planes[1];

    output_buf.length = 1;
    output_buf.m.planes = output_planes;
    xioctl(fd, VIDIOC_QUERYBUF, &output_buf);

    void *output_start = mmap(NULL, output_buf.m.planes[0].length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, output_buf.m.planes[0].m.mem_offset);

    // 启动 OUTPUT 流
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    xioctl(fd, VIDIOC_STREAMON, &type);

    // 设置 CAPTURE（解码帧）格式
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    fmt.fmt.pix_mp.width = WIDTH;
    fmt.fmt.pix_mp.height = HEIGHT;
    fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
    fmt.fmt.pix_mp.num_planes = 1;
    xioctl(fd, VIDIOC_S_FMT, &fmt);

    // 请求 CAPTURE buffer
    struct v4l2_requestbuffers req2 = {
        .count = BUFFER_COUNT,
        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
        .memory = V4L2_MEMORY_MMAP
    };
    xioctl(fd, VIDIOC_REQBUFS, &req2);

    // mmap capture buffers
    struct buffer capture_buffers[BUFFER_COUNT];
    for (int i = 0; i < BUFFER_COUNT; i++) {
        struct v4l2_buffer buf = {
            .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
            .memory = V4L2_MEMORY_MMAP,
            .index = i
        };
        struct v4l2_plane planes[1];
        buf.length = 1;
        buf.m.planes = planes;
        xioctl(fd, VIDIOC_QUERYBUF, &buf);

        capture_buffers[i].length = buf.m.planes[0].length;
        capture_buffers[i].start = mmap(NULL, buf.m.planes[0].length, PROT_READ | PROT_WRITE,
                                            MAP_SHARED, fd, buf.m.planes[0].m.mem_offset);
        xioctl(fd, VIDIOC_QBUF, &buf);
    }

    // 启动 CAPTURE 流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    xioctl(fd, VIDIOC_STREAMON, &type);

    // 准备 SDL2 显示
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window *win = SDL_CreateWindow("Decoded Output", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
                                       WIDTH, HEIGHT, 0);
    SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, 0);
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_NV12,
                                             SDL_TEXTUREACCESS_STREAMING, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height);

    // 发送数据到解码器并显示输出帧
    while (1) {
        // 读取一帧数据（这里简单假设每次读取 4KB）
        output_planes[0].bytesused = getNalu(output_start);
        if (output_planes[0].bytesused <= 0) break;

        // 提交 H.264 数据
        if (ioctl(fd, VIDIOC_QBUF, &output_buf) < 0) {
            perror("VIDIOC_QBUF (output)");
            continue;
        }

        // 获取解码帧
        struct v4l2_buffer cbuf = {0};
        struct v4l2_plane cplanes[1];
        cbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        cbuf.memory = V4L2_MEMORY_MMAP;
        cbuf.m.planes = cplanes;
        cbuf.length = 1;

        while (ioctl(fd, VIDIOC_DQBUF, &cbuf) == 0) {
            // 将 NV12 数据显示
            void *nv12 = capture_buffers[cbuf.index].start;
            SDL_UpdateTexture(texture, NULL, nv12, WIDTH);
            SDL_RenderClear(renderer);
            SDL_RenderCopy(renderer, texture, NULL, NULL);
            SDL_RenderPresent(renderer);

            // 重新放入 buffer
            xioctl(fd, VIDIOC_QBUF, &cbuf);

            //SDL_Delay(20); // 简单限速
        }

        // dequeue output buffer
        while (ioctl(fd, VIDIOC_DQBUF, &output_buf) < 0) {
            SDL_Delay(5); // 简单限速
        }

        SDL_PollEvent(NULL); // 允许关闭窗口
        //SDL_Delay(5); // 简单限速
    }

    close(fd);
    SDL_Quit();
    return 0;
}
